package me.magicall.test.reflect.proxy;

import java.io.Serializable;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;

class BeanImplProxy implements InvocationHandler, Serializable {

	private static final long serialVersionUID = 2527482961295077077L;

	private final Map<String, Object> fields = new HashMap<String, Object>();
	private final String[][] primaryKeys;

	public BeanImplProxy(final String[][] primaryKeys) {
		super();
		this.primaryKeys = primaryKeys;
	}

	public Object invoke(final Object proxy, final Method method, final Object[] args) {
		final String methodName = method.getName();
		//这三个方法比较特殊,要处理一下
		if (methodName.equals("toString")) {//相当于调用proxy.toString()
			return toString(proxy);
		}
		if (methodName.equals("equals")) {//相当于调用proxy.equals(args[0])
			return equals(proxy, args[0]);
		}
		if (methodName.equals("hashCode")) {//相当于调用proxy.hashCode()
			return hashCode(proxy);
		}

		if (methodName.startsWith(Util.GET)) {
			final String fieldName = Util.getterOrSetterNameToFieldName(methodName);
			return fields.get(fieldName);
		} else if (methodName.startsWith(Util.SET)) {
			final String fieldName = Util.getterOrSetterNameToFieldName(methodName);
			final Object value = args[0];
			//检验setter和getter的类型是否匹配
			Method getter = null;
			try {
				getter = proxy.getClass().getMethod(Util.GET + methodName.substring(Util.SET_LEN), Util.EMPTY_CLASS_ARR);
			} catch (final SecurityException e) {
			} catch (final NoSuchMethodException e) {
			}
			if (getter == null || getter.getReturnType().isAssignableFrom(value.getClass())) {
				fields.put(fieldName, value);
			} else {
				throw new ClassCastException();
			}
		}
		return null;
	}

	public void setValue(final String fieldName, final Object value) {
		fields.put(fieldName, value);
	}

	private int hashCode(final Object proxy) {
		int rt = 0;
		for (final String[] primaryKeyGroup : primaryKeys) {
			for (final String s : primaryKeyGroup) {
				final Object v = fields.get(s);
				if (v == null) {
					rt = 0;
					break;
				} else {
					rt = rt * 31 + v.hashCode();
				}
			}
		}
		return rt;
	}

	private boolean equals(final Object proxy, final Object o) {
		if (proxy == o) {
			return true;
		}
		final Class<?> clazz1 = proxy.getClass();
		final Class<?> clazz2 = o.getClass();
		if (clazz1 != clazz2////类型不同,有可能是不同的动态代理生成的不同的代理类,或者一个是代理类,一个是客户代码自己的实现类.不能就此返回false
				&& !Arrays.equals(clazz1.getInterfaces(), clazz2.getInterfaces())) {
			return false;
		}
		//下面来比较受pk指定的字段的值是否相等
		for (final String[] primaryKeyGroup : primaryKeys) {
			boolean needCompareNextPKGroup = true;
			for (final String s : primaryKeyGroup) {
				final Object v1 = fields.get(s);
				try {
					final Method method = clazz2.getMethod(Util.fieldNameToGetterName(s), Util.EMPTY_CLASS_ARR);
					final Object v2 = method.invoke(o);
					if (v1 == null) {
						if (v2 != null) {
							return false;
						}
					} else {
						if (!v1.equals(v2)) {
							return false;
						} else {
							needCompareNextPKGroup = false;
						}
					}
				} catch (final SecurityException e) {
					e.printStackTrace();
					return false;
				} catch (final NoSuchMethodException e) {
					e.printStackTrace();
					return false;
				} catch (final IllegalArgumentException e) {
					e.printStackTrace();
					return false;
				} catch (final IllegalAccessException e) {
					e.printStackTrace();
					return false;
				} catch (final InvocationTargetException e) {
					e.printStackTrace();
					return false;
				}
			}
			if (!needCompareNextPKGroup) {
				return true;
			}
		}
		return true;
	}

	/**
	 * @param proxy
	 * @return 注:其实返回的字符串也是正一个json格式的字符串
	 */
	private String toString(final Object proxy) {
		final Class<?> _interface = proxy.getClass().getInterfaces()[0];//我们自己代理的类的接口只有一个
		final StringBuilder sb = new StringBuilder(_interface.getSimpleName());
		final int hashCode = proxy.hashCode();
		sb.append(hashCode < 0 ? (hashCode == Integer.MIN_VALUE ? Integer.MAX_VALUE : -hashCode) : hashCode)//
				.append("={");
		for (final Map.Entry<?, ?> e : fields.entrySet()) {
			sb.append(e.getKey()).append(":").append(e.getValue()).append(",");
		}
		return sb.deleteCharAt(sb.length() - 1)//删除最后多加的逗号
				.append("}").toString();
	}
}